/***************************************************************************
 *   Copyright (C) 2008 by Jesse Crossen   *
 *   jesse.crossen@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "listener.h"

Listener::Listener(QObject *parent)
 : QObject(parent)
{
  // set defaults
  stream = NULL;
  currentDevice = 0;
  
  // initialize the portaudio engine
  paCall(Pa_Initialize());
  // get a list of potential audio source devices
  int numDevices = Pa_GetDeviceCount();
  // get info for them
  const PaDeviceInfo *deviceInfo;
  for(int i = 0; i < numDevices; i++) {
    deviceInfo = Pa_GetDeviceInfo(i);
    // if the device does not support any input channels, we can't listen to it
    if (! (deviceInfo->maxInputChannels > 0)) continue;
    // if this is the default device, select it for input
    if (strncmp(deviceInfo->name, "default", 8) == 0) 
      currentDevice = Devices.size();
    // add the device to the list
    DeviceInfo d;
    d.name = QString(deviceInfo->name);
    d.index = i;
    Devices << d;
  }
}

Listener::~Listener()
{
  // stop the stream
  StopListening();
  // stop the portaudio engine
  paCall(Pa_Terminate());
}

// fill a combobox for selecting audio inputs
void Listener::FillDeviceCombo(QComboBox * combo) {
  for(int i = 0; i < Devices.size(); i++) {
    combo->addItem(Devices.at(i).name);
  }
  combo->setCurrentIndex(currentDevice);
}

// start listening to incoming audio and return whether input started
int Listener::Listen() {
  // if we were already listening, stop
  StopListening();
  // request input on the currently selected channel
  PaStreamParameters inputParameters;
  inputParameters.channelCount = 1;
  inputParameters.device = Devices[currentDevice].index;
  inputParameters.hostApiSpecificStreamInfo = NULL;
  inputParameters.sampleFormat = paFloat32;
  inputParameters.suggestedLatency = 
      Pa_GetDeviceInfo(Devices[currentDevice].index)->defaultLowInputLatency ;
  inputParameters.hostApiSpecificStreamInfo = NULL;
  // open the stream
  if(! paCall(Pa_OpenStream(&stream, &inputParameters, NULL,
                            SAMPLE_RATE, FRAMES_PER_BUFFER, paNoFlag, 
                            paCallback, (void *)this))) return(0);
  // start listening to the stream
  if (! paCall(Pa_StartStream(stream))) return(0);
  // success
  return(1);
}
// stop listening to incoming audio
void Listener::StopListening() {
  if (stream != NULL) {
    paCall(Pa_StopStream(stream));
    paCall(Pa_CloseStream(stream));
    stream = NULL;
  }
}

// listen to a different device
void Listener::setDevice(int index) {
  if (index < 0) index = 0;
  if (index >= Devices.size()) index = Devices.size() - 1;
  
  if (currentDevice != index) {
    // change the source device
    currentDevice = index;
    // if we were listening to a device, start listening to the new one
    if (stream != NULL) Listen();
  }
}

// wrap calls to portaudio functions to report errors 
//  and return whether the call worked
int Listener::paCall(PaError err) {
  if (err == paNoError) return(1);
  std::cerr << "PortAudio error: " << Pa_GetErrorText(err) << std::endl;
  return(0);
}
